// 原理,简单的underscor, 
// 闭包,自执行函数
// (function(){
// }())
// 函数式编程
(function(){
  // 基础架构已经差不多, 简易版,没有实现链式调用等等
  var root = this;
  var _ = {};
  root._ = _;
  // 在这里定义自己的方法
  _.reverse = (string) => {
    return string.split('').reverse().join('')
  }
}())
let a = _.reverse('hello');
console.log(a);
// jequery 也是自执行函数
// underscore.js 其实就是个对象方法
// 架子抢建好了
// 解决了数据源 迭代器函数
// (function (root) {
//   // 2. 构造函数, 构造函数传入obj,就是为了处理类似unique方法的
//   var _ = function (obj) {
//     // 不知道它要创建什么函数,这里就判断它是不是要创建构造函数
//     // instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
//     // 如果不是构造函数就创建普通函数
//     if (!(this instanceof _)) {
//       return new _(obj);
//     }
//     // 数据源
//     this.wrap = obj;
//   }
//   //1. 下面两种map方法是相同的
//   _.map = function () {
//     console.log('1');
//     // 和下面的_.prototype[key] 不是同一个函数,调用的是两个函数对象,如何解决下面问题
//     // _.prototype[key] = function () {
//     //   console.log(2);
//     // }
//   }
//   // map上挂载到原型链实例对象上
//   _.prototype.map = function () {
//     console.log('hello map');
//   }
//   // 数据去重, callback的使用
//   _.unique = function (array, callback) {
//     var result = [];
//     var i = 0;
//     for (; i < array.length; i++) {
//       var target = callback ? callback(array[i]) : array[i];
//       if (result.indexOf(target) === -1) {
//         result.push(target);
//       }
//     }
//     return result;
//   }
//   // 处理数组中的属性,找到属性
//   _.each = function (array, callback) {
//     console.log('=====', array);
//     var i = 0;
//     for (;i < array.length; i++) {
//       // TODO: callback.call
//       callback.call(array, array[i]);
//     }
//   }
//   // 处理传进来的静态属性,返回一个数组
//   _.functions = function(obj) {
//     var result = [];
//     for (var key in obj) {
//       result.push(key);
//     }
//     return result;
//   }

//     //2. 如何避免上面被创建两遍
//   // 2.1 找到所有的静态属性,然后放到一个数组里,如[map, unique, ....], 
//   // 2.2 遍历数组 把这个属性扩展到原型里面一份, 如_.prototype[item], item就是map, unique
//   // 搭架子实现
//   // underscore.js 用混入来实现
//   _.mixin = function (obj) {
//     // 找到静态属性, obj是数组
//     _.each(_.functions(obj), function (key) {
//       // 和上面的map或其他属性不是同一个函数,调用的是两个函数对象,
//       // 如何解决下面问题, 问题根本只需要关注静态属性的输出
//       // _.prototype[key] = function () {
//       //   console.log(2);
//       // }
//       //1. 解决方法如下: // 这样就用了同一个函数,打印的就一样了
//       var func = obj[key];
//       _.prototype[key] = function () {
//         //2. 拿到数据源和迭代器函数来处理,
//         var argData = [this.wrap];
//         // 拿到方法arguments
//         console.log(this.wrap, arguments[0]);
//         // 将数据源和迭代器函数合并
//         // 不管你是一个函数还是多个函数,下面的方法已经处理好了
//         Array.prototype.push.apply(argData, arguments);
//         // func();
//         // 数据源 迭代器函数 argData 存放的就是数据源和迭代器函数
//         func.apply(this, argData);
//       }
//     });
//   }
//   _.mixin(_);
//   root._ = _;
// })(this)
// 解决数据流的问题
// 链式调用,调用了这个函数还可以继续执行下面的函数
// 这里通过max来实现
(function (root) {
  // 2. 构造函数, 构造函数传入obj,就是为了处理类似unique方法的
  var _ = function (obj) {
    // 不知道它要创建什么函数,这里就判断它是不是要创建构造函数
    // instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
    // 如果不是构造函数就创建普通函数
    if (!(this instanceof _)) {
      return new _(obj);
    }
    // 数据源
    this.wrap = obj;
  }
  // result 处理链式调用 
  var result = function (instance, obj) {
    // 如果需要链式调用判断, 改变数据源,数据源变成处理的结果
    if (instance._chain) {
      instance.wrap = obj;
      return instance;
    }
    return obj;
  }
  // 为原型扩展方法,必须写在构造方法下面
  _.prototype.value = function () {
    return this.wrap;
  }
  //1. 下面两种map方法是相同的
  _.map = function () {
    console.log('1');
    // 和下面的_.prototype[key] 不是同一个函数,调用的是两个函数对象,如何解决下面问题
    // _.prototype[key] = function () {
    //   console.log(2);
    // }
  }
  // 扩展max的方法 TODO: 可以从underscore.js 找到一个属性来这里去实现
  // args 是上一个工序处理后的结果
  _.max = function (args) {
    args.push('max', 'long');
    return args;
  }
  // 扩展chain的方法
  // 开启链式调用 
  _.chain = function (obj) { // 数据源
    // 数据源传来underscore
    // instance 开启underscore 
    var instance = _(obj); // 特殊的实例对象
    instance._chain = true; // 特殊的属性,开启链式调用的凭证
    return instance;
  }
  // map上挂载到原型链实例对象上
  _.prototype.map = function () {
    console.log('hello map');
  }
  // 数据去重, callback的使用
  _.unique = function (array, callback) {
    var result = [];
    var i = 0;
    for (; i < array.length; i++) {
      var target = callback ? callback(array[i]) : array[i];
      if (result.indexOf(target) === -1) {
        result.push(target);
      }
    }
    return result;
  }
  // 处理数组中的属性,找到属性
  _.each = function (array, callback) {
    console.log('=====', array);
    var i = 0;
    for (;i < array.length; i++) {
      // TODO: callback.call
      callback.call(array, array[i]);
    }
  }
  // 处理传进来的静态属性,返回一个数组
  _.functions = function(obj) {
    var result = [];
    for (var key in obj) {
      result.push(key);
    }
    return result;
  }
    //2. 如何避免上面被创建两遍
  // 2.1 找到所有的静态属性,然后放到一个数组里,如[map, unique, ....], 
  // 2.2 遍历数组 把这个属性扩展到原型里面一份, 如_.prototype[item], item就是map, unique
  // 搭架子实现
  // underscore.js 用混入来实现
  _.mixin = function (obj) {
    // 找到静态属性, obj是数组
    _.each(_.functions(obj), function (key) {
      // 和上面的map或其他属性不是同一个函数,调用的是两个函数对象,
      // 如何解决下面问题, 问题根本只需要关注静态属性的输出
      // _.prototype[key] = function () {
      //   console.log(2);
      // }
      //1. 解决方法如下: // 这样就用了同一个函数,打印的就一样了
      var func = obj[key];
      _.prototype[key] = function () {
        //2. 拿到数据源和迭代器函数来处理,
        var argData = [this.wrap];
        // 拿到方法arguments
        console.log(this.wrap, arguments[0]);
        // 将数据源和迭代器函数合并
        // 不管你是一个函数还是多个函数,下面的方法已经处理好了
        Array.prototype.push.apply(argData, arguments);
        // func();
        // 数据源 迭代器函数 argData 存放的就是数据源和迭代器函数
        // func.apply(this, argData);
        // 返回对象合并, 静态属性返回什么就返回什么
        // return func.apply(this, argData);
        // 开启链式调用 
        return result(this, func.apply(this, argData));
      }
    });
  }
  _.mixin(_);
  root._ = _;
})(this)